Skip to content

perf(settings): lazy load sub-settings pages to optimize load time#2358

Merged
bajrangCoder merged 6 commits into
mainfrom
perf/lazy-load-settings
Jun 22, 2026
Merged

perf(settings): lazy load sub-settings pages to optimize load time#2358
bajrangCoder merged 6 commits into
mainfrom
perf/lazy-load-settings

Conversation

@bajrangCoder

Copy link
Copy Markdown
Member

Eagerly instantiating all 9 sub-settings screens causes massive layout overhead when opening the settings menu, especially on lower-end devices. By using ECMAScript getters on uiSettings to lazy-load these pages on demand, the initial open load time drops by ~93% (from ~981ms to ~70ms on a 6x CPU throttle).

Eagerly instantiating all 9 sub-settings screens causes massive layout
overhead when opening the settings menu, especially on lower-end devices.
By using ECMAScript getters on `uiSettings` to lazy-load these pages on demand,
the initial open load time drops by ~93% (from ~981ms to ~70ms on a 6x CPU throttle).
@greptile-apps

greptile-apps Bot commented Jun 21, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR optimizes the settings page open time by converting the 9 sub-settings pages from eager to lazy initialization, and separately eliminates a forced reflow in PageHandler by caching scroll position via a passive scroll listener instead of reading it synchronously in replaceEl().

  • mainSettings.js: Each sub-settings page is now defined as a non-enumerable getter on appSettings.uiSettings that self-replaces with an enumerable data property after first access; the key in instantiated guard correctly handles falsy return values.
  • settingsPage.js: A new getSettingsPages helper uses Object.keys (returns only already-instantiated, enumerable entries) for restore and Object.getOwnPropertyNames (includes non-enumerable lazy getters) for search, with ??= caching to avoid re-triggering all initialisers on every keystroke.
  • wcPage.js: Scroll position is now captured passively on scroll events rather than read synchronously during page replacement, removing a layout-blocking forced reflow on every page transition.

Confidence Score: 5/5

Safe to merge; the lazy-loading mechanism is correctly implemented and the search/restore helpers are properly scoped to avoid unintended eager initialisation.

The core lazy-loading logic is sound: getters self-replace with data properties after first access, the key in instantiated guard avoids repeat initialisation on falsy returns, and ??= caching keeps the search path efficient after the first query. The only finding is an anonymous scroll listener in wcPage.js that cannot be explicitly removed, leaving a cleanup gap in remove() — a minor housekeeping concern with no impact on correctness for long-lived settings pages.

src/components/WebComponents/wcPage.js — the anonymous scroll listener added in the PageHandler constructor has no cleanup path in remove().

Important Files Changed

Filename Overview
src/settings/mainSettings.js Introduces lazy-loading getters for 9 sub-settings pages via Object.defineProperty; getters self-replace with enumerable data properties after first access, guarded correctly with key in instantiated.
src/components/settingsPage.js Adds getSettingsPages helper that uses Object.keys (non-lazy) or Object.getOwnPropertyNames (lazy) to avoid triggering uninitialised getters during restore, with ??= caching to avoid re-initialising all pages on every search keystroke.
src/components/WebComponents/wcPage.js Moves scroll-position capture from synchronous read in replaceEl() (forced reflow) to a passive capture-phase event listener; the listener is anonymous and cannot be removed, leaving a cleanup gap in remove().

Sequence Diagram

%%{init: {'theme': 'neutral'}}%%
sequenceDiagram
    participant U as User
    participant MS as mainSettings()
    participant US as appSettings.uiSettings
    participant SP as Sub-Settings Page

    U->>MS: Open Settings
    MS->>US: Set main-settings (eager)
    MS->>US: defineProperty(lazy getter, enumerable:false) x9

    note over U,SP: Pages NOT yet instantiated

    U->>US: Navigate to sub-page (e.g. editor-settings)
    US->>SP: getter fires → initializer()
    SP-->>US: page instance returned
    US->>US: redefine as data property (enumerable:true)
    SP-->>U: Page shown

    U->>US: Type in search box (first keystroke)
    US->>US: getOwnPropertyNames → all 9 keys
    US->>SP: Trigger remaining lazy getters (batch init)
    SP-->>US: page instances cached in settingsPages[]
    US-->>U: Search results

    U->>US: Subsequent search keystrokes
    US->>US: settingsPages already cached
    US-->>U: Search results (no re-init)
Loading
%%{init: {'theme': 'base', 'themeVariables': {"darkMode": true, "background": "#0d1117", "primaryColor": "#21262d", "primaryTextColor": "#e6edf3", "primaryBorderColor": "#8b949e", "lineColor": "#8b949e", "textColor": "#e6edf3", "edgeLabelBackground": "#161b22", "actorBkg": "#21262d", "actorBorder": "#8b949e", "actorTextColor": "#e6edf3", "actorLineColor": "#8b949e", "signalColor": "#8b949e", "signalTextColor": "#e6edf3", "noteBkgColor": "#373320", "noteBorderColor": "#d4a72c", "noteTextColor": "#f0e6c0", "labelBoxBkgColor": "#21262d", "labelBoxBorderColor": "#8b949e", "labelTextColor": "#e6edf3", "loopTextColor": "#e6edf3", "activationBkgColor": "#30363d", "activationBorderColor": "#8b949e"}}}%%
sequenceDiagram
    participant U as User
    participant MS as mainSettings()
    participant US as appSettings.uiSettings
    participant SP as Sub-Settings Page

    U->>MS: Open Settings
    MS->>US: Set main-settings (eager)
    MS->>US: defineProperty(lazy getter, enumerable:false) x9

    note over U,SP: Pages NOT yet instantiated

    U->>US: Navigate to sub-page (e.g. editor-settings)
    US->>SP: getter fires → initializer()
    SP-->>US: page instance returned
    US->>US: redefine as data property (enumerable:true)
    SP-->>U: Page shown

    U->>US: Type in search box (first keystroke)
    US->>US: getOwnPropertyNames → all 9 keys
    US->>SP: Trigger remaining lazy getters (batch init)
    SP-->>US: page instances cached in settingsPages[]
    US-->>U: Search results

    U->>US: Subsequent search keystrokes
    US->>US: settingsPages already cached
    US-->>U: Search results (no re-init)
Loading

Reviews (3): Last reviewed commit: "fix united page issue" | Re-trigger Greptile

Comment thread src/settings/mainSettings.js
Comment thread src/settings/mainSettings.js
@bajrangCoder

This comment was marked as outdated.

@bajrangCoder

This comment was marked as outdated.

@bajrangCoder bajrangCoder requested a review from deadlyjack June 22, 2026 17:20
@bajrangCoder bajrangCoder merged commit 206ee2c into main Jun 22, 2026
10 checks passed
@github-project-automation github-project-automation Bot moved this from Backlog to Done in The Code Board - Acode Jun 22, 2026
@bajrangCoder bajrangCoder deleted the perf/lazy-load-settings branch June 22, 2026 17:46
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: Done

Development

Successfully merging this pull request may close these issues.

2 participants